package com.dingwen.treasure.base.controller;

import com.dingwen.treasure.base.pojo.vo.Result;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * {@code BaseCrudController}封装基础单表增删改查 </br>
 * 基于数据库乐观锁保证接口幂等性 </br>
 * 若需要接口权限等自定义实现可进行重写 </br>
 * rest风格优化，参数拼接问题不方便处理，参考GitHub,ApiFox接口文档
 *
 * <p>共3种实现：</p>
 * <ol>
 *     <li>基于 mybatis</li>
 *     <li>基于 tk mybatis</li>
 *     <li>TODO 基于 jpa</li>
 *     <li>TODO 基于 jpa 的 mongoDB</li>
 *     <li> mongoDB 基于 mongoTemplate</li>
 * </ol>
 *
 * <p> rest示例如下：<p>
 *
 * <ul>
 *     <li>GET   {API_PREFIX}                   无条件返回全部List</li>
 *     <li>GET   {API_PREFIX}/{id}              根据主键查询单个</li>
 *     <li>POST  {API_PREFIX}/ids               携带请求体参数主键查询 示例：ids [1,2,3] ，集合长度至少为2</li>
 *     <li>GET   {API_PREFIX}/count             获取数据表总记录条数</li>
 *     <li>GET   {API_PREFIX}/exists/{id}       根据对象 id 查询是否存在 </li>
 *     <li>GET   {API_PREFIX}/page              无条件分页查询</li>
 *
 *     <li>PUT  {API_PREFIX}/{id}               修改单个实体，对象主键必须存在</li>
 *     <li>PUT  {API_PREFIX}/patch  请求体       批量修改，每一个对象的主键必须存在否则回滚</li>
 *     <li>POST {API_PREFIX} 请求体（D）          新增单个实体</li>
 *     <li>POST {API_PREFIX}/path               批量添加</li>
 *
 *
 *     <li>DELETE {API_PREFIX}/{id}             根据主键删除记录</li>
 *     <li>DELETE {API_PREFIX}/ids              携带请求参数主键删除,参数示例：ids [1,2,3] ，集合长度至少为2</li>
 *
 *     <li>POST {API_PREFIX}/search/all  请求体  根据请求体参数{@code Map<String,Object} 进行动态请求    </li>
 *     <li>POST {API_PREFIX}/search/page 请求体  分页搜索指定条件下的列表数据</li>
 *     <li>POST {API_PREFIX}/search/first 请求体 搜索指定条件的第一条记录</li>
 * <ul/>
 *
 * <p> 泛型说明： </p>
 * <ol>
 *     <li> D: dto 数据传输对象</li>
 * </ol>
 *
 * <p> 注意已存在到 mapper 和新加的路径冲突问题 </p>
 *
 * @author dingwen
 * @date 2021.06.30 09:44
 * @since 1.0.0-SNAPSHOT
 */
@Validated
public interface BaseCrudController<D> {

    /* --------------------------------------------查询--------------------------------------------*/

    /**
     * 根据Id查询，返回单个实体
     *
     * @param id 数据表主键
     * @return {@link Result}
     */
    Result<?> findById(@NotNull(message = "查询主键不能为空") Serializable id);


    /**
     * 根据List<id>查找实体
     *
     * @param ids id 传参示例： 1,2,3 集合长度至少为2
     * @return {@link Result}
     */
    Result<?> findByIds(@NotNull(message = "参数ids不能为空") @Size(min = 2, message = "主键集合必须大于等于2") List<Serializable> ids);


    /**
     * 无条件查询所有
     *
     * @return {@link Result}
     */
    Result<?> findAll();

    /**
     * 无条件分页查询
     *
     * @param pageSize 页面大小
     * @param pageNum  页面num
     * @return {@link Result}
     */
    Result<?> page(Integer pageSize, Integer pageNum);


    /**
     * 获取数据表总记录条数
     *
     * @return {@link Result}
     */
    Result<?> count();

    /**
     * 通过 id 查询是否存在 </br>
     * <ol>
     *     <li>true: 存在</li>
     *     <li>false: 不存在</li>
     * </ol>
     *
     * @param id 主键id
     * @return {@link Result}
     */
    Result<?> existsById(@NotNull(message = "查询主键不能为空") Serializable id);


    /* --------------------------------------------搜索--------------------------------------------*/

    /**
     * 搜索所有符合条件的列表（支持单表、多表联查）
     *
     * @param params 搜索参数
     * @return {@link Result}
     */
    default Result<?> searchAll(Map<String, Object> params) {
        return null;
    }


    /**
     * 搜索所有符合条件的列表（支持单表、多表联查）
     *
     * @param params 参数
     * @return {@link Result}
     */
    default Result<?> search(Map<String, Object> params) {
        return null;
    }


    /**
     * 搜索符合条件的第一条记录
     *
     * @param params 参数
     * @return {@link Result}
     */
    default Result<?> searchFirst(Map<String, Object> params) {
        return null;
    }


    /**
     * 搜索符合条件的记录条数
     *
     * @param params 参数
     * @return {@link Result}
     */
    default Result<?> searchCount(Map<String, Object> params) {
        return null;
    }



    /* --------------------------------------------修改--------------------------------------------*/

    /**
     * 修改,对象 id ,主键必须有值,默认获取 t 中的 id 属性
     *
     * @param dto 数据传输对象
     * @return {@link Result}
     */
    Result<?> modify(D dto);

    /**
     * 修改,对象 id ,主键必须有值
     *
     * @param dto            数据传输对象
     * @param fieldNameForId 指定Id 字段名称
     * @param id             主键id（符合rest接口风格规范）
     * @return {@link Result}<{@link ?}>
     */
    Result<?> modify(D dto, Serializable id, String fieldNameForId);


    /**
     * 修改
     * 修改,批量修改,对象主键必须要有值,,默认获取 t 中的 id 属性
     *
     * @param dtos           数据传输对象列表
     * @param fieldNameForId 指定Id 字段名称
     * @return {@link Result}
     */
    Result<?> batchModify(List<D> dtos, String fieldNameForId);

    /**
     * 修改,批量修改,对象主键必须要有值
     *
     * @param dtos 数据传输对象列表
     * @return {@link Result}
     */
    Result<?> batchModify(List<D> dtos);



    /* --------------------------------------------新增--------------------------------------------*/

    /**
     * 保存
     *
     * @param dto 数据传输对象
     * @return {@link Result}
     */
    Result<?> save(D dto);


    /**
     * 批量保存
     *
     * @param dtos 数据传输对象列表
     * @return {@link Result}
     */
    Result<?> batchSave(List<D> dtos);



    /* --------------------------------------------删除--------------------------------------------*/

    /**
     * 删除
     *
     * @param id id
     * @return {@link Result}
     */
    Result<?> removeById(@NotNull(message = "主键不能为空") Serializable id);


    /**
     * 根据主键集合批量删除
     *
     * @param ids id
     * @return {@link Result}
     */
    Result<?> batchRemoveByIds(@NotNull(message = "参数ids不能为空") @Size(min = 2, message = "主键集合必须大于等于2") List<Serializable> ids);


    /**
     * 默认开启 BO 到 VO 的转换
     *
     * @return {@link Boolean}
     */
    default Boolean enableBoToVo() {
        return Boolean.TRUE;
    }
}
